#
#   EvilGreg.py
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; version 2 of the License.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   Copyright
#   Author: Nicholas F. Hoover
#   Contributors: Salvatore S. Gionfriddo
#   Created: 2007.06.18
#   Last Modified: 2007.07.24
#

import pygame
from GameObject import GameObject
from Character import Character
from Animation import Animation
from PowerUp import PowerUp

class EvilGreg(Character):

#########
# STATS #
#########
    MAX_HUNGER = 100
    MIN_HUNGER = 0
    START_HUNGER = 40

    STATUS_TICKS_BETWEEN_UPDATES = 20
    HUNGER_DAMAGE_PER_TICK = 2
    REGEN_AMOUNT_PER_TICK = 1

    STARVATION_DAMAGE = 4

    STARVATION_MIN = 0
    STARVATION_MAX = 20
    
    HEARTBURN_MIN  = 80
    HEARTBURN_END  = 70

    MIN_GAS = 0
    MAX_GAS = 100
    GAS_COST = 15

    STATUS_UPDATES_PER_SFX = 5

##############
# ANIMATIONS #
##############

    NUM_ANIMATIONS = 11
    (IDLE,
     WALK_UP,
     WALK_DOWN,
     WALK_SIDE,
     PUNCH,
     KICK,
     EAT,
     FLAME,
     GAS,
     PICKUP,
     USE) = range(NUM_ANIMATIONS)

    ACTIONS = [PUNCH, KICK, EAT, FLAME, GAS, PICKUP, USE]
    
    LEFT = NUM_ANIMATIONS

    ###############
    # FRAME_TICKS #
    ###############
    IDLE_FRAME_TICKS   = 4
    WALK_FRAME_TICKS   = 2
    PUNCH_FRAME_TICKS  = [1,2,1] #1 * 3 frames = 3  updates for 2 kills .66 efficiency
    KICK_FRAME_TICKS   = [1,2,3,2,1] #2 * 5 frames = 10 updates for 7 kills .7 efficiency
    EAT_FRAME_TICKS    = [1,1,2,2,2,1]
    FLAME_FRAME_TICKS  = [2,3,2,5]
    GAS_FRAME_TICKS    = 2
    PICKUP_FRAME_TICKS = [1,1,2,2,1]
    USE_FRAME_TICKS    = 1
    
    ##############
    # KEY FRAMES #
    ##############
    PUNCH_KEY_FRAME = 1
    KICK_KEY_FRAME = 2
    EAT_KEY_FRAME = 3
    FLAME_KEY_FRAME = 3
    GAS_KEY_FRAME = 2
    PICKUP_KEY_FRAME = 3
    USE_KEY_FRAME = 1

    #########################
    # ATTACK COLLISION DATA #
    #########################
    PUNCH_OFFSET = (10,50)
    PUNCH_SIZE   = (20,10)
    KICK_OFFSET  = (15,60)
    KICK_SIZE    = (15,20)
    EAT_OFFSET   = (10,80)
    EAT_SIZE     = (12,12)
    PICKUP_OFFSET   = (10,80)
    PICKUP_SIZE     = (12,12)
    
    ###################
    # ANIMATION TABLE #
    ###################
    ANIMATION_TABLE = [
        (IDLE,      'EG_Idle',     IDLE_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_UP,   'EG_WalkUp',   WALK_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_DOWN, 'EG_WalkDown', WALK_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (WALK_SIDE, 'EG_WalkSide', WALK_FRAME_TICKS,  Animation.NO_KEY_FRAME),
        (PUNCH,     'EG_Punch',    PUNCH_FRAME_TICKS, PUNCH_KEY_FRAME),
        (KICK,      'EG_Kick',     KICK_FRAME_TICKS,  KICK_KEY_FRAME),
        (EAT,       'EG_Eat',      EAT_FRAME_TICKS,   EAT_KEY_FRAME),
        (FLAME,     'EG_Flame',    FLAME_FRAME_TICKS, FLAME_KEY_FRAME),
        (GAS,       'EG_Gas',      GAS_FRAME_TICKS,   GAS_KEY_FRAME),
        (PICKUP,    'EG_Pickup',   PICKUP_FRAME_TICKS,PICKUP_KEY_FRAME),
        (USE,       'EG_Use',      USE_FRAME_TICKS,   USE_KEY_FRAME),
        ]

    #############
    # MOVEMENTS #
    #############
    MOVEMENTS = [(0,0),   #IDLE
                 (0,-12), #WALK_UP
                 (0,12),  #WALK_DOWN
                 (12,0),  #WALK_SIDE
                 (0,0),   #PUNCH
                 (0,0),   #KICK
                 (0,0),   #EAT
                 (0,0),   #FLAME
                 (0,0),   #GAS
                 (0,0),   #PICKUP
                 (0,0),   #USE
                 ]

    DEFAULT_ANIMATION = IDLE


###########
# ATTACKS #    
###########
    PUNCH_DAMAGE = 200  #Kill 2 basic kids
    KICK_DAMAGE  = 700  #Kill 7 basic kids
    FLAME_DAMAGE = 2000 #Kill 20 basic kids per animation frame
    FLAME_COST = 10
    GAS_DAMAGE = 1000
    
########
# MISC #
########
    MAX_POWERUPS_EAT = 3
    MAX_POWERUPS_PICKUP = 1
    DEFAULT_POSITION = (300,300)
    RECT_SIZE = (60,100)

    COLLISION_RECT_SIZE = (0,0)
    COLLISION_RECT_POSITION = (20,95)


#############
# FUNCTIONS #
#############
    def __init__(self, fps = 30):
        Character.__init__(self, fps)
        self.reset()

    def reset(self):
        Character.reset(self)
        #status
        self.hunger = self.START_HUNGER
        
        self.heartBurn = False
        self.gas = EvilGreg.MIN_GAS
        self.statusUpdateCount = 0 #hunger/regen
        self.sfxUpdateCount = 0
        self.effects = []

        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing
        
        #inventory
        self.inventory = range(PowerUp.NUM_ANIMATIONS)
        for i in range(PowerUp.NUM_ANIMATIONS):
            self.inventory[i] = 0
        
###########
# Updates #
###########
    def update(self, collision):
        if self.updateCurrentAnimation():
            for action in EvilGreg.ACTIONS:
                self.checkForStopAction(action)

            #update the image to be displayed
            self.updateCurrentImage()

            #I like to move it, move it. Move it, move it.
            #HACK
            movement = self.MOVEMENTS[self.getAnimationType()]
            if self.currentAnimation == EvilGreg.WALK_SIDE + EvilGreg.LEFT:
                movement = (-movement[0], movement[1])
            (x,y) = (self.rect.left,self.rect.top)
            if self.rect.right + movement[0] <= 825 and self.rect.left + movement[0] >= -25: # mad hax
                x += movement[0]
            if self.rect.bottom + movement[1] <= 625 and self.rect.top + movement[1] >= -25: # mad hax
                y +=  movement[1]
            self.setPosition((x,y))

            self.doStatusUpdate()
            
    def doStatusUpdate(self):
        self.statusUpdateCount += 1
        if self.statusUpdateCount == self.STATUS_TICKS_BETWEEN_UPDATES:
            self.statusUpdateCount = 0
            self.doRegen()
            self.doHunger()
            self.doEffects()
        elif self.statusUpdateCount == self.STATUS_TICKS_BETWEEN_UPDATES/2:
            self.sfxUpdateCount += 1
            if self.sfxUpdateCount == self.STATUS_UPDATES_PER_SFX:
                self.playSound('EG_Misc')

    def doRegen(self):
        self.incHealth(self.REGEN_AMOUNT_PER_TICK)
 
    def doHunger(self):
        self.decHunger(self.HUNGER_DAMAGE_PER_TICK)
        if self.isStarving():
            self.decHealth(self.STARVATION_DAMAGE)
        elif self.hunger >= self.HEARTBURN_MIN:
            self.setHeartBurn(True)
        elif self.hunger < self.HEARTBURN_END:
            self.setHeartBurn(False)
    
    def addEffect(self, effect):
        #apply instant effect
        if effect.i_change:
            EvilGreg.__dict__[effect.i_change](self, effect.i_change_amount)
        #add to effect list
        self.effects.append(effect)
        
    def doEffects(self):
        for effect in self.effects[:]:
            #check effect age
            if effect.age >= effect.life:
                #apply end effect
                if effect.e_change:
                    EvilGreg.__dict__[effect.e_change](self, effect.e_change_amount)
                #remove from effect list
                self.effects.remove(effect)
            else:
                #increment effect age
                effect.age += 1
                #apply durational effect
                if effect.change:
                    EvilGreg.__dict__[effect.change](self, effect.change_amount)

#########
# STATS #
#########
    def incHunger(self,amount):
        self.hunger += amount
        if self.hunger > self.MAX_HUNGER:
            self.hunger = self.MAX_HUNGER

    def decHunger(self,amount):
        prevHunger = self.hunger
        self.hunger -= amount
        if self.hunger < self.MIN_HUNGER: self.hunger = self.MIN_HUNGER

        if prevHunger > self.HEARTBURN_END and self.hunger < self.HEARTBURN_END:
            self.playSound('EG_Hungry')
        elif prevHunger > self.STARVATION_MAX and self.isStarving():
            self.playSound('EG_Starving')

    
    def incHealth(self, amount):
        Character.incHealth(self, amount)
    def decHealth(self, amount):
        prevHealth = self.health
        Character.decHealth(self, amount)
        if prevHealth > 20 and self.health <= 20:
            self.playSound('EG_LowHealth')

    def setHeartBurn(self, status):
        if not self.heartBurn and status:
            self.playSound('EG_Heartburn')
        self.heartBurn = status

    def incGas(self, amount):
        self.gas += amount
        if self.gas > self.MAX_GAS: self.gas = self.MAX_GAS

    def decGas(self, amount):
        self.gas -= amount
        if self.gas < self.MIN_GAS: self.gas = self.MIN_GAS
        
    def getStats(self):
        return (self.health, self.hunger, self.heartBurn, self.gas)
    
    def isStarving(self):
        return self.hunger >= self.STARVATION_MIN and self.hunger <= self.STARVATION_MAX

    def isSatiated(self):
        return self.hunger == self.MAX_HUNGER

#############
# INVENTORY #
#############

    def _useItem(self, item):
        #decrement count
        self.inventory[item] -= 1
        #apply effect
        powerup = PowerUp(0, item)
        for effect in powerup.effects:
            self.addEffect(effect)        
            
    def addItem(self, item):
        #increment count
        self.inventory[item] += 1

    def viewItemCount(self, item):
        return self.inventory[item]

###########
# ACTIONS #
###########

    def walk(self,direction, facing = None):
        if self.canAct():
            self.setAnimation(direction, facing)

        if facing != None: self.nextFacing = facing
        else: self.nextFacing = self.facing
        self.nextAction = direction

    def stopWalk(self, direction, facing = None):
        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing
        if facing == None: facing = self.facing
        if self.currentAnimation == direction + facing:
            self.startIdling()

    def punch(self):
        if self.canAct():
            self.setAnimation(self.PUNCH)
            self.playSound('EG_Punch')


    def kick(self):
        if self.canAct():
            self.setAnimation(self.KICK)
            self.playSound('EG_Kick')
      
    def eat(self):
        if self.canAct():
            self.setAnimation(self.EAT)
    
    def useItem(self, item):
        if self.canAct() and self.inventory[item] > 0:
            self.setAnimation(self.USE)
            self._useItem(item)
            if item == PowerUp.DRUGS:
                self.playSound('EG_Drugs')
            else:
                self.playSound('EG_Eat')
            
    def pickup(self):
        if self.canAct():
            self.setAnimation(self.PICKUP)

    def flame(self):
        if self.canAct() and self.heartBurn:
            self.setAnimation(EvilGreg.FLAME)
            self.decHunger(EvilGreg.FLAME_COST)
            self.playSound('EG_Flame')

    def gasAttack(self):
        if self.canAct() and self.gas >= EvilGreg.GAS_COST:
            self.setAnimation(EvilGreg.GAS)
            self.decGas(EvilGreg.GAS_COST)
            self.playSound('Flame')
                

    def checkForStopAction(self, action):
        if self.isActionComplete(action):
            self.doNextAction()
            
    def doNextAction(self):
        self.facing = self.nextFacing
        self.setAnimation(self.nextAction)
        
    def clearNextAction(self):
        self.nextAction = EvilGreg.IDLE
        self.nextFacing = self.facing
        self.doNextAction()

    def startIdling(self):
        self.setAnimation(self.IDLE)
        
    def canAct(self):
        return self.currentAnimation - self.facing <= self.WALK_SIDE
        
    def createPunchSprite(self):
        punchSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            punchSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.PUNCH_OFFSET[0], self.rect.top + EvilGreg.PUNCH_OFFSET[1]), EvilGreg.PUNCH_SIZE)
        else:
            punchSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.PUNCH_OFFSET[0] - EvilGreg.PUNCH_SIZE[0], self.rect.top + EvilGreg.PUNCH_OFFSET[1]), EvilGreg.PUNCH_SIZE)
        return punchSprite

    def createKickSprite(self):
        kickSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            kickSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.KICK_OFFSET[0], self.rect.top + EvilGreg.KICK_OFFSET[1]), EvilGreg.KICK_SIZE)
        else:
            kickSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.KICK_OFFSET[0] - EvilGreg.KICK_SIZE[0], self.rect.top + EvilGreg.KICK_OFFSET[1]), EvilGreg.KICK_SIZE)
        return kickSprite

    def createEatSprite(self):
        eatSprite = pygame.sprite.Sprite()
        if self.facing == EvilGreg.RIGHT:
            eatSprite.rect = pygame.Rect((self.rect.centerx + EvilGreg.EAT_OFFSET[0], self.rect.top + EvilGreg.EAT_OFFSET[1]), EvilGreg.EAT_SIZE)
        else:
            eatSprite.rect = pygame.Rect((self.rect.centerx - EvilGreg.EAT_OFFSET[0] - EvilGreg.EAT_SIZE[0], self.rect.top + EvilGreg.EAT_OFFSET[1]), EvilGreg.EAT_SIZE)
        return eatSprite


class DeadEvilGreg(GameObject):
    
    NUM_ANIMATIONS = 1
    DEAD = 0


    LEFT = NUM_ANIMATIONS

    ###################
    # ANIMATION TABLE #
    ###################
    ANIMATION_TABLE = [
        (DEAD,        'EG_Dead', 1, Animation.NO_KEY_FRAME),
        ]

    DEFAULT_ANIMATION = DEAD


    RECT_SIZE = (100, 60)

    def __init__(self):
        GameObject.__init__(self)
        

